guava Ordering总结

学习Ordering的背景是这样的:去年我在负责搜索业务开发的时候,将所有需要搜索以及需要展示的字段通通插入到ElasticSearch的索引中保存下来。这样做的好处是当Es搜索出数据之后可以直接将数据组织起来返回给前端,不再需要进行数据库的查询,减少了数据库的压力;坏处也显而易见,当数据库中的表或者需要展示的数据有变动则需要重新进行数据的索引。综合来看这样做的弊大于利。所以现在改成Es中只索引需要搜索的数据,搜索返回后用结果的id去数据库中再进行一次查询。因为id在数据库中一般都是主键或者是有唯一索引的字段,因此这样的一次查询耗费的时间是很少的,这样避免了Es中的数据频繁的要重新索引。

这样做唯一需要注意的是:数据库查询后的数据顺序要和Es中搜索出来的数据顺序保持一致,这样才能保证相关性高的数据在前面展示。

order by field

mysql提供了一种order by field的方法来进行自定义排序。

打个比方,在Es中搜索评论数据,返回数据的id顺序为2, 4, 6, 5, 3, 1,我们可以使用语句

1
select * from comment where auto_pk in (2, 4, 6, 5, 3, 1) order by field(auto_pk, 2, 4, 6, 5, 3, 1)

以特定的顺序来查询评论数据。

这种方式倒也不复杂,但是将特定的排序操作放在数据库中来操作使得sql语句冗余,不利于维护。

于是我们直接在代码中来进行排序。guava的Ordering类的子类ExplicitOrdering正好满足我们的需要。在此总结一下Ordering类的使用。

Ordering

Ordering是guava中用于增强排序功能的类。提供了许多静态方法来简化我们的排序代码。

需要说明的是对于java 8的用户来说,里面的绝大多数功能都可以使用StreamComparator来代替。下文我也会说明如何来进行这种代替。

allEqual()

allEqual()方法将列表中所有的元素都视作相同的元素,即不改变列表中元素原来的顺序。

使用方式如下:

1
2
3
Ordering<Object> objectOrdering = Ordering.allEqual();
numbers.sort(objectOrdering);
System.out.println(numbers);

如果使用Java 8,可以使用以下的代码来代替:

1
2
numbers.sort((a, b) -> 0);
System.out.println(numbers);

arbitrary()

arbitrary()将列表中所有元素随意排列。注意它并不是随机排列,而是依赖每个元素的identityHashCode值来进行比较。

使用方式如下:

1
2
3
Ordering<Object> arbitrary = Ordering.arbitrary();
numbers.sort(arbitrary);
System.out.println(numbers);

compare(T left, T right)

compare(T left, T right)按某个顺序对leftright元素比较大小。如果left < right,返回负整数;如果left = right,返回0;如果left > right,返回正整数。

compound(Comparator<? super U> secondaryComparator)

将当前的OrderingsecondaryComparator组合成一个新的Ordering

假设有一个学生列表,每个学生分别有分数(score)和标识(id)。现在要先对学生的分数进行排序,如果分数相同再对id进行排序,其代码如下所示:

1
2
3
4
Ordering<Student> scoreOrder = Ordering.natural().onResultOf(Student::getScore);
Ordering<Student> idOrder = Ordering.natural().reverse().onResultOf(Student::getId);
Ordering<Student> compoundOrder = scoreOrder.compound(idOrder);
studentList.sort(compoundOrder);

如果使用Java 8,可以使用以下的代码来代替:

1
thisComparator.thenComparing(secondaryComparator)

compound(Iterable<? extends Comparator<? super T>> comparators)

comparators中的所有比较器组合成一个新的Ordering

还是按上面需求:先对学生的分数进行排序,如果分数相同再对id进行排序,其代码如下所示:

1
2
3
4
5
Ordering<Student> scoreOrder = Ordering.natural().onResultOf(Student::getScore);
Ordering<Student> idOrder = Ordering.natural().reverse().onResultOf(Student::getId);

Ordering<Student> compoundOrder = Ordering.compound(Arrays.asList(scoreOrder, idOrder));
studentList.sort(compoundOrder);

如果使用Java 8,可以使用以下的代码来代替:

1
Comparator.thenComparing(Comparator)

或者

1
comparatorCollection.stream().reduce(Comparator::thenComparing).get()

explicit(List valuesInOrder)

explicit(List<T> valuesInOrder)方法按照valuesInOrder中的顺序对列表进行排序。

假设我们要对学生列表按照id的特定顺序进行排序,可以使用如下代码:

1
2
3
4
5
6
7
8
List<Integer> idOrder = new ArrayList<>();
for (int i = 0; i < 100; i++) {
idOrder.add(i);
}
idOrder.sort(Ordering.arbitrary());
System.out.println(idOrder);
Ordering<Student> studentOrdering = Ordering.explicit(idOrder).onResultOf(Student::getId);
ImmutableList<Student> students = studentOrdering.immutableSortedCopy(studentList);

explicit(T leastValue, T… remainingValuesInOrder)

explicit(T leastValue, T... remainingValuesInOrder)也是将列表按特定的顺序来排序,唯一不同的是它将leastValue作为最小值,其他的值按remainingValuesInOrder的顺序来排序。示例如下:

1
2
3
4
5
List<Integer> numbers = Arrays.asList(5, 1, 10, 9, 20);
Ordering<Integer> explicitOrder = Ordering.explicit(1, 5, 9, 10, 20);

List<Integer> list = explicitOrder.immutableSortedCopy(numbers);
System.out.println(list);

from(Comparator comparator)

comparator包装成Ordering

greatestOf(Iterable iterable, int k)

按照特定的排序返回k个最大的值,示例如下:

1
2
Ordering<Comparable> natural = Ordering.natural();
List<Integer> integers = natural.greatestOf(numbers, 3);

如果使用Java 8,可以使用以下的代码来代替:

1
Streams.stream(iterable).collect(Comparators.greatest(k, thisComparator))

immutableSortedCopy(Iterable elements)

elements排序之后返回一个不可变(immutable)的列表,elements不发生改变。示例如下:

1
2
Ordering<Comparable> natural = Ordering.natural();
ImmutableList<Integer> integers = natural.immutableSortedCopy(numbers);

isOrdered(Iterable<? extends T> iterable)

isOrdered方法判断iterable按照特定的顺序是否是有序的,及后面的元素要大于等于前面的元素。示例如下:

1
2
3
4
5
6
List<Integer> num = Arrays.asList(5, 1, 1, 2, 9, 4);
Ordering<Comparable> natural = Ordering.natural();
num.sort(natural);
System.out.println(num);
System.out.println(natural.isOrdered(num));
System.out.println(natural.isStrictlyOrdered(num));

如果使用Java 8,可以使用以下的代码来代替:

1
Comparators.isInOrder(Iterable, Comparator)

isStrictlyOrdered(Iterable<? extends T> iterable)

isStrictlyOrdered方法判断iterable按照特定的顺序是否是严格有序的,及后面的元素要大于前面的元素。

如果使用Java 8,可以使用以下的代码来代替:

1
Comparators.isInStrictOrder(Iterable, Comparator)

leastOf(Iterable iterable, int k)

按照特定的排序返回k个最小的值,示例如下:

1
2
Ordering<Comparable> natural = Ordering.natural();
List<Integer> integers = natural.leastOf(numbers, 3);

如果使用Java 8,可以使用以下的代码来代替:

1
Streams.stream(iterable).collect(Comparators.least(k, thisComparator))

lexicographical()

将列表元素按字典序进行排序。一个典型的字典序如下所示:[] < [1] < [1, 1] < [1, 2] < [2]

1
2
3
4
5
6
7
8
9
10
11
Ordering<Iterable<Integer>> lexicographical = Ordering.natural().lexicographical();
List<List<Integer>> lists = new ArrayList<>();
lists.add(Arrays.asList());
lists.add(Arrays.asList(1));
lists.add(Arrays.asList(1, 1));
lists.add(Arrays.asList(1, 2));
lists.add(Arrays.asList(1, 2, 3));
lists.add(Arrays.asList(2));

List<List<Integer>> es = lexicographical.immutableSortedCopy(lists);
System.out.println(es);

如果使用Java 8,可以使用以下的代码来代替:

1
Comparators.lexicographical(Comparator)

natural()

按自然顺序对元素进行排序。

1
2
3
Ordering<Integer> natural = Ordering.natural();
numbers.sort(natural);
System.out.println(numbers);

如果使用Java 8,可以使用以下的代码来代替:

1
Comparator.naturalOrder()

nullsFirst()

将所有的null元素都排列到列表的开头。

1
2
3
4
5
6
7
8
Ordering<Comparable> nullsFirstOrdering = Ordering.natural().nullsFirst();
List<Integer> list = new ArrayList<>(numbers);
list.add(null);
list.add(null);
list.add(null);

list.sort(nullsFirstOrdering);
System.out.println(list);

如果使用Java 8,可以使用以下的代码来代替:

1
Comparator.nullsFirst(thisComparator)

nullsLast()

将所有的null元素都排列到列表的末尾。

1
2
3
4
5
6
7
8
List<Integer> list = new ArrayList<>(numbers);
list.add(null);
list.add(null);
list.add(null);

Ordering<Comparable> nullsLastOrdering = Ordering.natural().nullsLast();
list.sort(nullsLastOrdering);
System.out.println(list);

如果使用Java 8,可以使用以下的代码来代替:

1
Comparator.nullsLast(thisComparator)

onResultOf(Function<F,? extends T> function)

将元素根据function返回的值进行排序。

如果使用Java 8,可以使用以下的代码来代替:

1
Comparator.comparing(function, thisComparator)

reverse()

将特定的顺序按逆序排列。

1
2
3
Ordering<Integer> natural = Ordering.natural();
numbers.sort(natural.reverse());
System.out.println(numbers);

如果使用Java 8,可以使用以下的代码来代替:

1
thisComparator.reversed()

sortedCopy(Iterable elements)

elements排序之后返回一个不可变(immutable)的列表,elements不发生改变。

usingToString()

将元素按照它toString()方法返回的值进行排序

如果使用Java 8,可以使用以下的代码来代替:

1
Comparator.comparing(Object::toString)

https://google.github.io/guava/releases/snapshot-jre/api/docs/